﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Collections;

namespace AndersLiu.PE
{
    /// <summary>
    /// Indicates a DOS header.
    /// </summary>
    /// <remarks>
    /// WinNT.h:IMAGE_DOS_HEADER.
    /// </remarks>
    public class ImageDosHeader : ImageData
    {
        public const ushort DosSignature = 0x5A4D;

        public DosSignatureField Signature { get; private set; }
        public U4Field PESignatureOffset { get; private set; }

        internal ImageDosHeader(PEFile image, uint offset)
            : base(image, offset, 0x40)
        {
            this.Signature = new DosSignatureField(image, offset);

            if (this.Signature.Value != DosSignature)
                throw new MissingDosSignatureException();

            this.PESignatureOffset = new U4Field(image, offset + 0x3C);
        }
    }

    public class DosSignatureField : U2Field
    {
        internal DosSignatureField(PEFile image, uint offset)
            : base(image, offset)
        {
        }

        protected override string GetValueString()
        {
            var rawData = GetRawData();
            var str = Encoding.ASCII.GetString(rawData);

            return string.Format("{0:X4}h ('{1}')", this.Value, str);
        }
    }

    public class PESignature : ImageData
    {
        public const uint NTSignature = 0x00004550;

        public PESignatureField Signature { get; private set; }

        internal PESignature(PEFile image, uint offset)
            : base(image, offset, 4)
        {
            this.Signature = new PESignatureField(image, offset);

            if (this.Signature.Value != NTSignature)
                throw new MissingPESignatureException();
        }
    }

    public class PESignatureField : U4Field
    {
        internal PESignatureField(PEFile image, uint offset)
            : base(image, offset)
        {
        }

        protected override string GetValueString()
        {
            var rawData = GetRawData();
            var str = Encoding.ASCII.GetString(rawData);

            return string.Format("{0:X8}h ('{1}')", this.Value, str);
        }
    }

    public class ImageFileHeader : ImageData
    {
        public ImageFileMachineField Machine { get; private set; }
        public U2Field NumberOfSections { get; private set; }
        public TimeDateStampField TimeDateStamp { get; private set; }
        public U4Field PointerToSymbolTable { get; private set; }
        public U4Field NumberOfSymbols { get; private set; }
        public U2Field OptionalHeaderSize { get; private set; }
        public ImageFileCharacteristicsField Characteristics { get; private set; }

        internal ImageFileHeader(PEFile image, uint offset)
            : base(image, offset)
        {
            var pos = offset;

            this.Machine = new ImageFileMachineField(image, pos);
            pos += this.Machine.RawDataSize;

            this.NumberOfSections = new U2Field(image, pos);
            pos += this.NumberOfSections.RawDataSize;

            this.TimeDateStamp = new TimeDateStampField(image, pos);
            pos += this.TimeDateStamp.RawDataSize;

            this.PointerToSymbolTable = new U4Field(image, pos);
            pos += this.PointerToSymbolTable.RawDataSize;

            this.NumberOfSymbols = new U4Field(image, pos);
            pos += this.NumberOfSymbols.RawDataSize;

            this.OptionalHeaderSize = new U2Field(image, pos);
            pos += this.OptionalHeaderSize.RawDataSize;

            this.Characteristics = new ImageFileCharacteristicsField(image, pos);
            pos += this.Characteristics.RawDataSize;

            SetSize(pos - offset);
        }
    }

    public class ImageFileMachineField : Field<ImageFileMachine>
    {
        internal ImageFileMachineField(PEFile image, uint offset)
            : base(image, offset, 2)
        {
        }

        protected override ImageFileMachine ParseValue(byte[] rawData)
        {
            return (ImageFileMachine)BitConverter.ToUInt16(rawData, 0);
        }

        protected override string GetValueString()
        {
            return EnumUtility.GetEnumStrig(this.Value);
        }
    }

    public class TimeDateStampField : Field<DateTime>
    {
        public uint RawValue { get; private set; }

        internal TimeDateStampField(PEFile image, uint offset)
            : base(image, offset, 4)
        {
        }

        protected override DateTime ParseValue(byte[] rawData)
        {
            var rawValue = BitConverter.ToUInt32(rawData, 0);
            this.RawValue = rawValue;
            return new DateTime(1970, 1, 1).AddSeconds(rawValue);
        }

        protected override string GetValueString()
        {
            return string.Format("{0:X8}h ({1})", this.RawValue, this.Value);
        }
    }

    public class ImageFileCharacteristicsField : Field<ImageFileCharacteristics>
    {
        internal ImageFileCharacteristicsField(PEFile image, uint offset)
            : base(image, offset, 2)
        {
        }

        protected override ImageFileCharacteristics ParseValue(byte[] rawData)
        {
            return (ImageFileCharacteristics)BitConverter.ToUInt16(rawData, 0);
        }

        protected override string GetValueString()
        {
            return EnumUtility.GetEnumStrig(this.Value);
        }
    }

    public abstract class ImageOptionalHeader : ImageData
    {
        public abstract ImageOptionalHeaderMagicField Magic { get; protected set; }
        public abstract U4Field NumberOfDataDirectories { get; protected set; }

        protected ImageOptionalHeader(PEFile image, uint offset)
            : base(image, offset)
        {
        }

        public const int MaxDataDirectories = 16;

        public static ImageOptionalHeader GetOptionalHeader(PEFile image, uint offset)
        {
            var magic = new ImageOptionalHeaderMagicField(image, offset);

            switch (magic.Value)
            {
                case ImageOptionalHeaderMagic.NtOptionalHeader32:
                    return new ImageOptionalHeader32(image, offset);
                case ImageOptionalHeaderMagic.NtOptionalHeader64:
                    return new ImageOptionalHeader64(image, offset);
                default:
                    throw new NotSupportedException("Unknown optional header.");
            }
        }
    }

    public class ImageOptionalHeader32 : ImageOptionalHeader
    {
        // Standard fields
        public override ImageOptionalHeaderMagicField Magic { get; protected set; }
        public U1Field LMajor { get; private set; }
        public U1Field LMinor { get; private set; }
        public U4Field CodeSize { get; private set; }
        public U4Field InitializedDataSize { get; private set; }
        public U4Field UninitializedDataSize { get; private set; }
        public RvaField EntryPointRva { get; private set; }
        public RvaField BaseOfCode { get; private set; }
        public RvaField BaseOfData { get; private set; }

        // NT-specific fields
        public U4Field ImageBase { get; private set; }
        public U4Field SectionAlignment { get; private set; }
        public U4Field FileAlignment { get; private set; }
        public U2Field OSMajor { get; private set; }
        public U2Field OSMinor { get; private set; }
        public U2Field UserMajor { get; private set; }
        public U2Field UserMinor { get; private set; }
        public U2Field SubSysMajor { get; private set; }
        public U2Field SubSysMinor { get; private set; }
        public U4Field Reserved { get; private set; }
        public U4Field ImageSize { get; private set; }
        public U4Field HeaderSize { get; private set; }
        public U4Field FileChecksum { get; private set; }
        public ImageSubsystemField SubSystem { get; private set; }
        public ImageDllCharacteristicsField DllFlags { get; private set; }
        public U4Field StackReserveSize { get; private set; }
        public U4Field StackCommitSize { get; private set; }
        public U4Field HeapReserveSize { get; private set; }
        public U4Field HeapCommitSize { get; private set; }
        public U4Field LoaderFlags { get; private set; }
        public override U4Field NumberOfDataDirectories { get; protected set; }

        // Data directories
        public DataDirectories DataDirectories { get; private set; }

        internal ImageOptionalHeader32(PEFile image, uint offset)
            : base(image, offset)
        {
            var pos = offset;

            // Standard fields
            this.Magic = new ImageOptionalHeaderMagicField(image, pos);
            pos += this.Magic.RawDataSize;

            this.LMajor = new U1Field(image, pos);
            pos += this.LMajor.RawDataSize;

            this.LMinor = new U1Field(image, pos);
            pos += this.LMinor.RawDataSize;

            this.CodeSize = new U4Field(image, pos);
            pos += this.CodeSize.RawDataSize;

            this.InitializedDataSize = new U4Field(image, pos);
            pos += this.InitializedDataSize.RawDataSize;

            this.UninitializedDataSize = new U4Field(image, pos);
            pos += this.UninitializedDataSize.RawDataSize;

            this.EntryPointRva = new RvaField(image, pos);
            pos += this.EntryPointRva.RawDataSize;

            this.BaseOfCode = new RvaField(image, pos);
            pos += this.BaseOfCode.RawDataSize;

            this.BaseOfData = new RvaField(image, pos);
            pos += this.BaseOfData.RawDataSize;

            // NT-specific fields
            this.ImageBase = new U4Field(image, pos);
            pos += this.ImageBase.RawDataSize;

            this.SectionAlignment = new U4Field(image, pos);
            pos += this.SectionAlignment.RawDataSize;

            this.FileAlignment = new U4Field(image, pos);
            pos += this.FileAlignment.RawDataSize;

            this.OSMajor = new U2Field(image, pos);
            pos += this.OSMajor.RawDataSize;

            this.OSMinor = new U2Field(image, pos);
            pos += this.OSMinor.RawDataSize;

            this.UserMajor = new U2Field(image, pos);
            pos += this.UserMajor.RawDataSize;

            this.UserMinor = new U2Field(image, pos);
            pos += this.UserMinor.RawDataSize;

            this.SubSysMajor = new U2Field(image, pos);
            pos += this.SubSysMajor.RawDataSize;

            this.SubSysMinor = new U2Field(image, pos);
            pos += this.SubSysMinor.RawDataSize;

            this.Reserved = new U4Field(image, pos);
            pos += this.Reserved.RawDataSize;

            this.ImageSize = new U4Field(image, pos);
            pos += this.ImageSize.RawDataSize;

            this.HeaderSize = new U4Field(image, pos);
            pos += this.HeaderSize.RawDataSize;

            this.FileChecksum = new U4Field(image, pos);
            pos += this.FileChecksum.RawDataSize;

            this.SubSystem = new ImageSubsystemField(image, pos);
            pos += this.SubSystem.RawDataSize;

            this.DllFlags = new ImageDllCharacteristicsField(image, pos);
            pos += this.DllFlags.RawDataSize;

            this.StackReserveSize = new U4Field(image, pos);
            pos += this.StackReserveSize.RawDataSize;

            this.StackCommitSize = new U4Field(image, pos);
            pos += this.StackCommitSize.RawDataSize;

            this.HeapReserveSize = new U4Field(image, pos);
            pos += this.HeapReserveSize.RawDataSize;

            this.HeapCommitSize = new U4Field(image, pos);
            pos += this.HeapCommitSize.RawDataSize;

            this.LoaderFlags = new U4Field(image, pos);
            pos += this.LoaderFlags.RawDataSize;

            this.NumberOfDataDirectories = new U4Field(image, pos);
            pos += this.NumberOfDataDirectories.RawDataSize;

            // Data directories
            this.DataDirectories = new DataDirectories(image, pos);
            pos += this.DataDirectories.RawDataSize;

            SetSize(pos - offset);
        }
    }

    public class ImageOptionalHeader64 : ImageOptionalHeader
    {
        // Standard fields
        public override ImageOptionalHeaderMagicField Magic { get; protected set; }
        public U1Field LMajor { get; private set; }
        public U1Field LMinor { get; private set; }
        public U4Field CodeSize { get; private set; }
        public U4Field InitializedDataSize { get; private set; }
        public U4Field UninitializedDataSize { get; private set; }
        public RvaField EntryPointRva { get; private set; }
        public RvaField BaseOfCode { get; private set; }

        // NT-specific fields
        public U8Field ImageBase { get; private set; }
        public U4Field SectionAlignment { get; private set; }
        public U4Field FileAlignment { get; private set; }
        public U2Field OSMajor { get; private set; }
        public U2Field OSMinor { get; private set; }
        public U2Field UserMajor { get; private set; }
        public U2Field UserMinor { get; private set; }
        public U2Field SubSysMajor { get; private set; }
        public U2Field SubSysMinor { get; private set; }
        public U4Field Reserved { get; private set; }
        public U4Field ImageSize { get; private set; }
        public U4Field HeaderSize { get; private set; }
        public U4Field FileChecksum { get; private set; }
        public ImageSubsystemField SubSystem { get; private set; }
        public ImageDllCharacteristicsField DllFlags { get; private set; }
        public U8Field StackReserveSize { get; private set; }
        public U8Field StackCommitSize { get; private set; }
        public U8Field HeapReserveSize { get; private set; }
        public U8Field HeapCommitSize { get; private set; }
        public U4Field LoaderFlags { get; private set; }
        public override U4Field NumberOfDataDirectories { get; protected set; }

        // Data directories
        public DataDirectories DataDirectories { get; private set; }

        internal ImageOptionalHeader64(PEFile image, uint offset)
            : base(image, offset)
        {
            var pos = offset;

            // Standard fields
            this.Magic = new ImageOptionalHeaderMagicField(image, pos);
            pos += this.Magic.RawDataSize;

            this.LMajor = new U1Field(image, pos);
            pos += this.LMajor.RawDataSize;

            this.LMinor = new U1Field(image, pos);
            pos += this.LMinor.RawDataSize;

            this.CodeSize = new U4Field(image, pos);
            pos += this.CodeSize.RawDataSize;

            this.InitializedDataSize = new U4Field(image, pos);
            pos += this.InitializedDataSize.RawDataSize;

            this.UninitializedDataSize = new U4Field(image, pos);
            pos += this.UninitializedDataSize.RawDataSize;

            this.EntryPointRva = new RvaField(image, pos);
            pos += this.EntryPointRva.RawDataSize;

            this.BaseOfCode = new RvaField(image, pos);
            pos += this.BaseOfCode.RawDataSize;

            // NT-specific fields
            this.ImageBase = new U8Field(image, pos);
            pos += this.ImageBase.RawDataSize;

            this.SectionAlignment = new U4Field(image, pos);
            pos += this.SectionAlignment.RawDataSize;

            this.FileAlignment = new U4Field(image, pos);
            pos += this.FileAlignment.RawDataSize;

            this.OSMajor = new U2Field(image, pos);
            pos += this.OSMajor.RawDataSize;

            this.OSMinor = new U2Field(image, pos);
            pos += this.OSMinor.RawDataSize;

            this.UserMajor = new U2Field(image, pos);
            pos += this.UserMajor.RawDataSize;

            this.UserMinor = new U2Field(image, pos);
            pos += this.UserMinor.RawDataSize;

            this.SubSysMajor = new U2Field(image, pos);
            pos += this.SubSysMajor.RawDataSize;

            this.SubSysMinor = new U2Field(image, pos);
            pos += this.SubSysMinor.RawDataSize;

            this.Reserved = new U4Field(image, pos);
            pos += this.Reserved.RawDataSize;

            this.ImageSize = new U4Field(image, pos);
            pos += this.ImageSize.RawDataSize;

            this.HeaderSize = new U4Field(image, pos);
            pos += this.HeaderSize.RawDataSize;

            this.FileChecksum = new U4Field(image, pos);
            pos += this.FileChecksum.RawDataSize;

            this.SubSystem = new ImageSubsystemField(image, pos);
            pos += this.SubSystem.RawDataSize;

            this.DllFlags = new ImageDllCharacteristicsField(image, pos);
            pos += this.DllFlags.RawDataSize;

            this.StackReserveSize = new U8Field(image, pos);
            pos += this.StackReserveSize.RawDataSize;

            this.StackCommitSize = new U8Field(image, pos);
            pos += this.StackCommitSize.RawDataSize;

            this.HeapReserveSize = new U8Field(image, pos);
            pos += this.HeapReserveSize.RawDataSize;

            this.HeapCommitSize = new U8Field(image, pos);
            pos += this.HeapCommitSize.RawDataSize;

            this.LoaderFlags = new U4Field(image, pos);
            pos += this.LoaderFlags.RawDataSize;

            this.NumberOfDataDirectories = new U4Field(image, pos);
            pos += this.NumberOfDataDirectories.RawDataSize;

            // Data directories
            this.DataDirectories = new DataDirectories(image, pos);
            pos += this.DataDirectories.RawDataSize;

            SetSize(pos - offset);
        }
    }

    public class ImageOptionalHeaderMagicField : Field<ImageOptionalHeaderMagic>
    {
        internal ImageOptionalHeaderMagicField(PEFile image, uint offset)
            : base(image, offset, 2)
        {
        }

        protected override ImageOptionalHeaderMagic ParseValue(byte[] rawData)
        {
            return (ImageOptionalHeaderMagic)BitConverter.ToUInt16(rawData, 0);
        }

        protected override string GetValueString()
        {
            return EnumUtility.GetEnumStrig(this.Value);
        }
    }

    public class ImageSubsystemField : Field<ImageSubsystem>
    {
        internal ImageSubsystemField(PEFile image, uint offset)
            : base(image, offset, 2)
        {
        }

        protected override ImageSubsystem ParseValue(byte[] rawData)
        {
            return (ImageSubsystem)BitConverter.ToUInt16(rawData, 0);
        }

        protected override string GetValueString()
        {
            return EnumUtility.GetEnumStrig(this.Value);
        }
    }

    public class ImageDllCharacteristicsField : Field<ImageDllCharacteristics>
    {
        internal ImageDllCharacteristicsField(PEFile image, uint offset)
            : base(image, offset, 2)
        {
        }

        protected override ImageDllCharacteristics ParseValue(byte[] rawData)
        {
            return (ImageDllCharacteristics)BitConverter.ToUInt16(rawData, 0);
        }

        protected override string GetValueString()
        {
            return EnumUtility.GetEnumStrig(this.Value);
        }
    }

    public class ImageDataDirectory : ImageData
    {
        public RvaField VirtualAddress { get; private set; }
        public U4Field Size { get; private set; }

        internal ImageDataDirectory(PEFile image, uint offset)
            : base(image, offset, 8)
        {
            this.VirtualAddress = new RvaField(image, offset);
            this.Size = new U4Field(image, offset + this.VirtualAddress.RawDataSize);
        }
    }

    public class DataDirectories : ImageData, IEnumerable<ImageDataDirectory>
    {
        public ImageDataDirectory Export { get { return this[DataDirectoryIndex.Export]; } }
        public ImageDataDirectory Import { get { return this[DataDirectoryIndex.Import]; } }
        public ImageDataDirectory Resource { get { return this[DataDirectoryIndex.Resource]; } }
        public ImageDataDirectory Exception { get { return this[DataDirectoryIndex.Exception]; } }
        public ImageDataDirectory Certificate { get { return this[DataDirectoryIndex.Certificate]; } }
        public ImageDataDirectory BaseRelocation { get { return this[DataDirectoryIndex.BaseRelocation]; } }
        public ImageDataDirectory Debug { get { return this[DataDirectoryIndex.Debug]; } }
        public ImageDataDirectory Copyright { get { return this[DataDirectoryIndex.Copyright]; } }
        public ImageDataDirectory GlobalPtr { get { return this[DataDirectoryIndex.GlobalPtr]; } }
        public ImageDataDirectory TLS { get { return this[DataDirectoryIndex.TLS]; } }
        public ImageDataDirectory LoadConfig { get { return this[DataDirectoryIndex.LoadConfig]; } }
        public ImageDataDirectory BoundImport { get { return this[DataDirectoryIndex.BoundImport]; } }
        public ImageDataDirectory IAT { get { return this[DataDirectoryIndex.IAT]; } }
        public ImageDataDirectory DelayImportDescriptor { get { return this[DataDirectoryIndex.DelayImportDescriptor]; } }
        public ImageDataDirectory CliHeader { get { return this[DataDirectoryIndex.CliHeader]; } }
        public ImageDataDirectory Reserved { get { return this[DataDirectoryIndex.Reserved]; } }

        public ImageDataDirectory this[DataDirectoryIndex index]
        {
            get { return _dirs[(int)index]; }
        }

        public int Count
        {
            get { return _dirs.Length; }
        }

        public IEnumerator<ImageDataDirectory> GetEnumerator()
        {
            for (var i = 0; i < _dirs.Length; i++)
                yield return _dirs[i];
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        internal DataDirectories(PEFile image, uint offset)
            : base(image, offset)
        {
            var pos = offset;

            for (var i = 0; i < _dirs.Length; i++)
            {
                _dirs[i] = new ImageDataDirectory(image, pos);
                pos += _dirs[i].RawDataSize;
            }

            SetSize(pos - offset);
        }

        private ImageDataDirectory[] _dirs =
            new ImageDataDirectory[ImageOptionalHeader.MaxDataDirectories];
    }

    public enum DataDirectoryIndex
    {
        Export,
        Import,
        Resource,
        Exception,
        Certificate,
        BaseRelocation,
        Debug,
        Copyright,
        GlobalPtr,
        TLS,
        LoadConfig,
        BoundImport,
        IAT,
        DelayImportDescriptor,
        CliHeader,
        Reserved,
    }

    public class DataDirectoryNames
    {
        public const string Export = "Export";
        public const string Import = "Import";
        public const string Resource = "Resource";
        public const string Exception = "Exception";
        public const string Certificate = "Certificate";
        public const string BaseRelocation = "BaseRelocation";
        public const string Debug = "Debug";
        public const string Copyright = "Copyright";
        public const string GlobalPtr = "GlobalPtr";
        public const string TLS = "TLS";
        public const string LoadConfig = "LoadConfig";
        public const string BoundImport = "BoundImport";
        public const string IAT = "IAT";
        public const string DelayImportDescriptor = "DelayImportDescriptor";
        public const string CliHeader = "CliHeader";
        public const string Reserved = "Reserved";

        public static string Get(DataDirectoryIndex index)
        {
            return _allNames[(int)index];
        }

        static string[] _allNames = new[]
        {
            Export,
            Import,
            Resource,
            Exception,
            Certificate,
            BaseRelocation,
            Debug,
            Copyright,
            GlobalPtr,
            TLS,
            LoadConfig,
            BoundImport,
            IAT,
            DelayImportDescriptor,
            CliHeader,
            Reserved,
        };
    }

    public class ImageSectionHeader : ImageData
    {
        public FixedSizeAsciiStringField Name { get; private set; }
        public U4Field VirtualSize { get; private set; }
        public U4Field VirtualAddress { get; private set; }
        public U4Field SizeOfRawData { get; private set; }
        public U4Field PointerToRawData { get; private set; }
        public U4Field PointerToRelocations { get; private set; }
        public U4Field PointerToLinenumbers { get; private set; }
        public U2Field NumberOfRelocations { get; private set; }
        public U2Field NumberOfLinenumbers { get; private set; }
        public ImageSectionCharacteristicsField Characteristics { get; private set; }

        internal ImageSectionHeader(PEFile image, uint offset)
            : base(image, offset)
        {
            var pos = offset;

            this.Name = new FixedSizeAsciiStringField(image, pos, 8);
            pos += this.Name.RawDataSize;

            this.VirtualSize = new U4Field(image, pos);
            pos += this.VirtualSize.RawDataSize;

            this.VirtualAddress = new U4Field(image, pos);
            pos += this.VirtualAddress.RawDataSize;

            this.SizeOfRawData = new U4Field(image, pos);
            pos += this.SizeOfRawData.RawDataSize;

            this.PointerToRawData = new U4Field(image, pos);
            pos += this.PointerToRawData.RawDataSize;

            this.PointerToRelocations = new U4Field(image, pos);
            pos += this.PointerToRelocations.RawDataSize;

            this.PointerToLinenumbers = new U4Field(image, pos);
            pos += this.PointerToLinenumbers.RawDataSize;

            this.NumberOfRelocations = new U2Field(image, pos);
            pos += this.NumberOfRelocations.RawDataSize;

            this.NumberOfLinenumbers = new U2Field(image, pos);
            pos += this.NumberOfLinenumbers.RawDataSize;

            this.Characteristics = new ImageSectionCharacteristicsField(image, pos);
            pos += this.Characteristics.RawDataSize;

            SetSize(pos - offset);
        }
    }

    public class SectionHeaders : ImageData, IEnumerable<ImageSectionHeader>
    {
        public ImageSectionHeader this[int index]
        {
            get
            {
                if (index < 0 || index >= this._sections.Length)
                    throw new SectionNotFoundException();

                return this._sections[index];
            }
        }

        public ImageSectionHeader this[string name]
        {
            get
            {
                for (var i = 0; i < this._sections.Length; i++)
                {
                    if (this[i].Name.Equals(name))
                        return this[i];
                }

                throw new SectionNotFoundException();
            }
        }

        public int Count
        {
            get
            {
                return this._sections.Length;
            }
        }

        public IEnumerator<ImageSectionHeader> GetEnumerator()
        {
            for (var i = 0; i < _sections.Length; i++)
                yield return _sections[i];
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        internal SectionHeaders(PEFile image, uint offset)
            : base(image, offset)
        {
            var pos = offset;
            var numSections = image.FileHeader.NumberOfSections.Value;

            _sections = new ImageSectionHeader[numSections];

            for (var i = 0; i < numSections; i++)
            {
                _sections[i] = new ImageSectionHeader(image, pos);
                pos += _sections[i].RawDataSize;
            }

            SetSize(pos - offset);
        }

        private ImageSectionHeader[] _sections;
    }

    public class ImageSectionCharacteristicsField : Field<ImageSectionCharacteristics>
    {
        internal ImageSectionCharacteristicsField(PEFile image, uint offset)
            : base(image, offset, 4)
        {
        }

        protected override ImageSectionCharacteristics ParseValue(byte[] rawData)
        {
            return (ImageSectionCharacteristics)BitConverter.ToUInt32(rawData, 0);
        }

        protected override string GetValueString()
        {
            return EnumUtility.GetEnumStrig(this.Value);
        }
    }
}
